#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
LSP与AST协调器
协调AST写回和LSP重命名操作，避免重复处理
"""

import os
import time
from typing import Dict, Set, List, Optional, Tuple
from dataclasses import dataclass, field
from threading import Lock
from enum import Enum

from ..utils_module.logger import ObfuscationLogger


class ProcessingMethod(Enum):
    """处理方法"""
    AST = "ast"          # AST重写
    LSP = "lsp"          # LSP重命名
    REGEX = "regex"      # 正则替换
    NONE = "none"        # 未处理


@dataclass
class SymbolProcessingRecord:
    """符号处理记录"""
    symbol: str
    file_path: str
    method: ProcessingMethod
    success: bool
    timestamp: float
    new_name: Optional[str] = None
    error_message: Optional[str] = None


@dataclass
class FileProcessingStatus:
    """文件处理状态"""
    file_path: str
    processed_symbols: Dict[str, ProcessingMethod] = field(default_factory=dict)
    pending_symbols: Set[str] = field(default_factory=set)
    ast_success: bool = False
    lsp_success: bool = False
    last_modified: float = 0.0


class LSPASTCoordinator:
    """LSP与AST协调器"""

    def __init__(self, logger: ObfuscationLogger = None):
        """
        初始化协调器

        Args:
            logger: 日志记录器
        """
        self.logger = logger
        self._lock = Lock()

        # 处理状态跟踪
        self.file_status: Dict[str, FileProcessingStatus] = {}
        self.symbol_records: List[SymbolProcessingRecord] = []

        # 处理策略配置
        self.prefer_ast = True  # 优先使用AST
        self.allow_fallback = True  # 允许回退
        self.skip_processed = True  # 跳过已处理的符号

        # 统计信息
        self.stats = {
            'ast_success': 0,
            'ast_failed': 0,
            'lsp_success': 0,
            'lsp_failed': 0,
            'lsp_skipped': 0,
            'regex_fallback': 0,
            'duplicate_avoided': 0
        }

    def should_process_with_ast(self, file_path: str, symbols: Set[str]) -> bool:
        """
        判断是否应该使用AST处理

        Args:
            file_path: 文件路径
            symbols: 待处理符号集合

        Returns:
            是否应该使用AST
        """
        with self._lock:
            # 获取文件状态
            status = self._get_or_create_file_status(file_path)

            # 如果AST之前失败过，且不允许回退，则不使用
            if status.ast_success is False and not self.allow_fallback:
                return False

            # 检查是否有未被处理过的符号
            unprocessed = symbols - set(status.processed_symbols.keys())

            # 如果有未处理的符号且优先AST，则使用AST
            return len(unprocessed) > 0 and self.prefer_ast

    def should_process_with_lsp(self, file_path: str, symbol: str) -> bool:
        """
        判断是否应该使用LSP处理特定符号

        Args:
            file_path: 文件路径
            symbol: 符号名称

        Returns:
            是否应该使用LSP
        """
        with self._lock:
            status = self._get_or_create_file_status(file_path)

            # 如果符号已经被AST成功处理，跳过LSP
            if symbol in status.processed_symbols:
                method = status.processed_symbols[symbol]
                if method == ProcessingMethod.AST and self.skip_processed:
                    self.stats['lsp_skipped'] += 1
                    if self.logger:
                        self.logger.log_operation(
                            "LSP协调",
                            f"跳过已被AST处理的符号: {symbol} in {file_path}"
                        )
                    return False

            # 如果符号未被处理或之前处理失败，使用LSP
            return True

    def record_ast_processing(self, file_path: str, symbols: Dict[str, str],
                             success: bool, error: Optional[str] = None) -> None:
        """
        记录AST处理结果

        Args:
            file_path: 文件路径
            symbols: 处理的符号映射 {原名: 新名}
            success: 是否成功
            error: 错误信息
        """
        with self._lock:
            if not symbols:
                return
            status = self._get_or_create_file_status(file_path)
            if success:
                status.ast_success = True
            elif not status.ast_success:
                status.ast_success = False
            status.last_modified = time.time()

            if success:
                # 记录成功处理的符号
                for original, new_name in symbols.items():
                    status.processed_symbols[original] = ProcessingMethod.AST
                    self._add_record(SymbolProcessingRecord(
                        symbol=original,
                        file_path=file_path,
                        method=ProcessingMethod.AST,
                        success=True,
                        timestamp=time.time(),
                        new_name=new_name
                    ))

                self.stats['ast_success'] += len(symbols)

                if self.logger:
                    self.logger.log_operation(
                        "AST处理",
                        f"成功处理 {len(symbols)} 个符号 in {file_path}"
                    )
            else:
                self.stats['ast_failed'] += 1
                if self.logger:
                    self.logger.log_error(f"AST处理失败: {file_path}, {error}")

    def record_lsp_processing(self, file_path: str, symbol: str,
                            new_name: str, success: bool,
                            error: Optional[str] = None) -> None:
        """
        记录LSP处理结果

        Args:
            file_path: 文件路径
            symbol: 原始符号
            new_name: 新名称
            success: 是否成功
            error: 错误信息
        """
        with self._lock:
            status = self._get_or_create_file_status(file_path)

            # 检查是否重复处理
            if symbol in status.processed_symbols:
                self.stats['duplicate_avoided'] += 1
                if self.logger:
                    self.logger.log_warning(
                        f"避免重复处理: {symbol} 已被 {status.processed_symbols[symbol]} 处理"
                    )
                return

            if success:
                status.processed_symbols[symbol] = ProcessingMethod.LSP
                status.lsp_success = True
                self.stats['lsp_success'] += 1

                self._add_record(SymbolProcessingRecord(
                    symbol=symbol,
                    file_path=file_path,
                    method=ProcessingMethod.LSP,
                    success=True,
                    timestamp=time.time(),
                    new_name=new_name
                ))

                if self.logger:
                    self.logger.log_operation(
                        "LSP处理",
                        f"成功: {symbol} -> {new_name} in {file_path}"
                    )
            else:
                self.stats['lsp_failed'] += 1
                if self.logger:
                    self.logger.log_error(f"LSP处理失败: {symbol} in {file_path}, {error}")

    def record_regex_fallback(self, file_path: str, symbols: Dict[str, str]) -> None:
        """
        记录正则回退处理

        Args:
            file_path: 文件路径
            symbols: 处理的符号映射
        """
        with self._lock:
            status = self._get_or_create_file_status(file_path)

            for original, new_name in symbols.items():
                if original not in status.processed_symbols:
                    status.processed_symbols[original] = ProcessingMethod.REGEX
                    self.stats['regex_fallback'] += 1

                    self._add_record(SymbolProcessingRecord(
                        symbol=original,
                        file_path=file_path,
                        method=ProcessingMethod.REGEX,
                        success=True,
                        timestamp=time.time(),
                        new_name=new_name
                    ))

            if self.logger:
                self.logger.log_operation(
                    "正则回退",
                    f"处理 {len(symbols)} 个符号 in {file_path}"
                )

    def get_processing_strategy(self, file_path: str,
                               symbols: Set[str]) -> Tuple[Set[str], Set[str], Set[str]]:
        """
        获取处理策略，将符号分配给不同的处理方法

        Args:
            file_path: 文件路径
            symbols: 待处理符号集合

        Returns:
            (AST处理集合, LSP处理集合, 跳过集合)
        """
        with self._lock:
            status = self._get_or_create_file_status(file_path)

            ast_symbols = set()
            lsp_symbols = set()
            skip_symbols = set()

            for symbol in symbols:
                if symbol in status.processed_symbols:
                    # 已处理，跳过
                    skip_symbols.add(symbol)
                elif self.prefer_ast and status.ast_success is not False:
                    # 优先AST且AST未失败
                    ast_symbols.add(symbol)
                else:
                    # 使用LSP
                    lsp_symbols.add(symbol)

            return ast_symbols, lsp_symbols, skip_symbols

    def merge_results(self, file_path: str) -> Dict[str, str]:
        """
        合并文件的所有处理结果

        Args:
            file_path: 文件路径

        Returns:
            最终的符号映射表
        """
        with self._lock:
            status = self.file_status.get(file_path)
            if not status:
                return {}

            # 从记录中提取符号映射
            result = {}
            for record in self.symbol_records:
                if record.file_path == file_path and record.success and record.new_name:
                    result[record.symbol] = record.new_name

            return result

    def clear_file_status(self, file_path: str) -> None:
        """
        清除文件处理状态

        Args:
            file_path: 文件路径
        """
        with self._lock:
            if file_path in self.file_status:
                del self.file_status[file_path]

            # 清除相关记录
            self.symbol_records = [
                r for r in self.symbol_records
                if r.file_path != file_path
            ]

    def get_statistics(self) -> Dict[str, int]:
        """
        获取统计信息

        Returns:
            统计信息字典
        """
        with self._lock:
            total_processed = (
                self.stats['ast_success'] +
                self.stats['lsp_success'] +
                self.stats['regex_fallback']
            )

            return {
                **self.stats,
                'total_processed': total_processed,
                'total_files': len(self.file_status),
                'total_records': len(self.symbol_records)
            }

    def reset_statistics(self) -> None:
        """重置统计信息"""
        with self._lock:
            for key in self.stats:
                self.stats[key] = 0

    def _get_or_create_file_status(self, file_path: str) -> FileProcessingStatus:
        """
        获取或创建文件状态

        Args:
            file_path: 文件路径

        Returns:
            文件处理状态
        """
        if file_path not in self.file_status:
            self.file_status[file_path] = FileProcessingStatus(file_path=file_path)
        return self.file_status[file_path]

    def _add_record(self, record: SymbolProcessingRecord) -> None:
        """
        添加处理记录

        Args:
            record: 处理记录
        """
        self.symbol_records.append(record)

        # 限制记录数量，防止内存过度使用
        max_records = 10000
        if len(self.symbol_records) > max_records:
            # 保留最新的记录
            self.symbol_records = self.symbol_records[-max_records:]

    def should_skip_file(self, file_path: str) -> bool:
        """
        判断是否应该跳过整个文件

        Args:
            file_path: 文件路径

        Returns:
            是否跳过
        """
        with self._lock:
            status = self.file_status.get(file_path)
            if not status:
                return False

            # 如果文件的所有方法都失败了，考虑跳过
            if status.ast_success is False and status.lsp_success is False:
                if self.logger:
                    self.logger.log_warning(f"跳过多次失败的文件: {file_path}")
                return True

            return False

    def get_unprocessed_symbols(self, file_path: str,
                               all_symbols: Set[str]) -> Set[str]:
        """
        获取未处理的符号

        Args:
            file_path: 文件路径
            all_symbols: 所有符号集合

        Returns:
            未处理的符号集合
        """
        with self._lock:
            status = self.file_status.get(file_path)
            if not status:
                return all_symbols

            processed = set(status.processed_symbols.keys())
            return all_symbols - processed
